/// Created by RongCheng on 2022/7/26.
import 'dart:ui';
import 'package:flutter/material.dart';

import 'notifier/notification.dart';

/// 监听拖拽的Widget实现拖拽动画
class DragGestureAnimation extends StatefulWidget {
  /// 计算和处理动画的widget
  const DragGestureAnimation({
    Key? key,
    required this.child,
    required this.pushAnimationController,
    required this.popAnimationController,
  }) : super(key: key);
  final Widget child;
  //飞出动画
  final AnimationController pushAnimationController;
  //返回动画
  final AnimationController popAnimationController;
  @override
  State<DragGestureAnimation> createState() => _DragGestureAnimationState();
}

class _DragGestureAnimationState extends State<DragGestureAnimation>
    with SingleTickerProviderStateMixin {
  /// 缩放
  final ValueNotifier<double> _scaleNotifier = ValueNotifier<double>(1.0);

  /// 位置偏移量 默认没有偏移（0，0）
  final ValueNotifier<Offset> _offsetNotifier =
      ValueNotifier<Offset>(Offset.zero);

  /// 透明度比例参考值：高度中心点, 拖拽到高度的一半背景完全透明
  final double transparencyRatioReference =
      MediaQueryData.fromWindow(window).size.height * 0.5;

  /// 缩放的比例参考值，拖拽整个高度才消失
  final double scaleRatioReference =
      MediaQueryData.fromWindow(window).size.height;

  /// 拖拽的无效距离，达到这个距离，page会直接pop
  final double _dragY = 100;

  /// 重置动画的控制器，拖拽距离不足时，动画返回原来的位置
  late AnimationController _resetAnimationController;

  /// 重置动画
  late Animation _resetAnimation;

  /// 记录最后一次缩放值
  double _resetScale = 0;

  /// 记录最后一次移动的累计偏移量
  Offset _resetOffset = Offset.zero;

  /// 记录最后一次移动的透明度 0 - 1 之间
  double _resetAnimationBound = 0;

  @override
  void initState() {
    super.initState();
    //_resetAnimation.value 从 0 变化到 1
    _resetAnimationController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 300))
      ..addListener(() {
        ///_resetAnimation.value 为0时，
        /// widget.pushAnimationController.value 回归成初始值 1
        /// _scaleNotifier.value 回归成初始值 1
        /// _offsetNotifier.value  回归成初始值 0
        // widget.pushAnimationController.value =
        //     _resetAnimation.value * (_resetAnimationBound + 1) -
        //         _resetAnimationBound;
        // _scaleNotifier.value =
        //     _resetAnimation.value * (_resetScale + 1) - _resetScale;

        //缩小的加回来
        widget.pushAnimationController.value = _resetAnimationBound +
            _resetAnimation.value * (1 - _resetAnimationBound);

        _scaleNotifier.value =
            _resetScale + _resetAnimation.value * (1 - _resetScale);
        //增大的减回来
        double dx = _resetOffset.dx - _resetAnimation.value * _resetOffset.dx;
        double dy = _resetOffset.dy - _resetAnimation.value * _resetOffset.dy;
        _offsetNotifier.value = Offset(dx, dy);
      });
    //CurvedAnimation 非线性曲线动画
    _resetAnimation = CurvedAnimation(
      parent: _resetAnimationController,
      curve: Curves.easeOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    // 监听，只处理DragNotification类型的通知
    return NotificationListener(
      onNotification: (notification) {
        if (notification is DragOnPanStartNotification) {
          _onPanStart(notification.details);
        } else if (notification is DragOnPanUpdateNotification) {
          _onPanUpdate(notification.details);
        } else if (notification is DragOnPanEndNotification) {
          _onPanEnd(notification.details);
        } else if (notification is DragOnPanCancelNotification) {
          _onPanCancel();
        } else if (notification is DragOnTapNotification) {
          _onTap();
        }
        //停止冒泡
        return true;
      },
      child: ValueListenableBuilder<Offset>(
        //位置监听
        valueListenable: _offsetNotifier,
        builder: (context, offset, child) {
          //缩放和移动整个页面  child: widget.child
          return Transform.translate(
            // 矩阵变换
            offset: offset,
            child: ValueListenableBuilder<double>(
              //缩放监听
              valueListenable: _scaleNotifier,
              builder: (context, scale, child) {
                return Transform.scale(
                  scale: scale,
                  //RepaintBoundary 如果没有变化避免重新绘制 CustomPainter
                  child: RepaintBoundary(
                    child: widget.child,
                  ),
                );
              },
            ),
          );
        },
      ),
    );
  }

  // 拖拽开始
  void _onPanStart(DragStartDetails details) {}

  /// 拖拽中 DragUpdateDetails details 每次相对上一次的点变化上加下减左减右加
  void _onPanUpdate(DragUpdateDetails details) {
    // 累计偏移量
    _offsetNotifier.value += details.delta;
    // 更新缩放比例
    _scaleNotifier.value =
        1 - ((_offsetNotifier.value.dy).abs() / scaleRatioReference);
    // 背景渐变
    widget.pushAnimationController.value =
        1 - ((_offsetNotifier.value.dy).abs() / transparencyRatioReference);
    // pop.value = push.value，动画衔接，更流畅，记录当前透明度
    widget.popAnimationController.value = widget.pushAnimationController.value;
  }

  // 拖拽结束
  void _onPanEnd(DragEndDetails details) {
    // 上下拖拽距离超过有效距离后，触发退出
    if ((_offsetNotifier.value.dy).abs() > _dragY) {
      _pop();
    } else {
      // 不足有效距离，回到原处，开启重置动画
      _resetScale = _scaleNotifier.value;

      _resetOffset = _offsetNotifier.value;

      _resetAnimationBound = widget.pushAnimationController.value;
      _resetAnimationController.forward(from: 0);
    }
  }

  // 取消拖拽
  void _onPanCancel() {}

  // 单击
  void _onTap() {
    _pop();
  }

  // 返回
  void _pop() {
    //widget.popAnimationController 从当前值用300毫秒变化到0 --- 背景慢慢消失
    widget.popAnimationController
        .animateTo(0, duration: const Duration(milliseconds: 300));

    //路由回退即执行 Hero 动画,
    widget.pushAnimationController.value = 1;
    Navigator.pop(context);
  }

  @override
  void dispose() {
    _resetAnimationController.dispose();
    super.dispose();
  }
}
